package com.lambkit.core;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.lambkit.core.annotion.*;
import com.lambkit.core.cache.ICache;
import com.lambkit.core.config.ConfigCenter;
import com.lambkit.core.config.ConfigCenterContainer;
import com.lambkit.core.pipeline.PipeLineContext;
import com.lambkit.util.Printer;

import java.util.*;

/**
 * 应用管理器
 * @author yangyong(孤竹行)
 */
public class Lambkit {
    private static final LambkitContext context = new LambkitContext();

    public static String version() {
        return "2.0";
    }

    public static LambkitContext context() {
        return context;
    }

    public static LambkitApp app() {
        return context.app();
    }

    public static LambkitApp app(String name)  {
        return context.app(name);
    }

    public static LambkitApp createApp(String name, Class<? extends LambkitApp> appClass)  {
        return context.createApp(name, appClass);
    }

    public static synchronized String createName(Class<? extends LambkitApp> appClass) {
        String name = LambkitContext.MAIN_APP;
        if(Lambkit.app() != null) {
            name = "app" + Lambkit.context().appSize() + "_" + appClass.getSimpleName() + "_" + RandomUtil.randomNumbers(4);
        }
        return name;
    }

    public static LambkitApp start(Class<?> target, String[] args) {
        TimeInterval timer = DateUtil.timer();
        printBanner();
        Lambkit.context().init();
        Lambkit.context().setTargetClass(target);

        LambkitServer lambkitServer = null;
        Server serverAnno = target.getAnnotation(Server.class);
        if(serverAnno != null) {
            Class<? extends LambkitServer> serverClasses = serverAnno.value();
            if(serverClasses!=null) {
                lambkitServer = ReflectUtil.newInstance(serverClasses);
            }
        } else {
            String serverClassName = context().getConfigContext("lambkit.server.class");
            //Printer.print(this, "starter", "context.json config lambkit.server.class={}.", serverClassName);
            if(StrUtil.isNotBlank(serverClassName)) {
                lambkitServer = ReflectUtil.newInstance(serverClassName);
            }
            if(lambkitServer==null) {
                String packageName = ClassUtil.getPackage(target);
                Set<Class<?>> serverApps = ClassUtil.scanPackageBySuper(packageName, LambkitServer.class);
                if(serverApps!=null) {
                    for(Class serverApp : serverApps) {
                        Class<LambkitServer> lambkitServerClass = serverApp;
                        lambkitServer = ReflectUtil.newInstance(lambkitServerClass);
                        break;
                    }
                }
            }
        }
        if(lambkitServer!=null) {
            System.out.println("Starting Lambkit " + version() + " with " + lambkitServer.getClass().getName());
            context().setBean(LambkitServer.class, lambkitServer);
            lambkitServer.start();
        } else {
            System.out.println("Starting Lambkit " + version());
            PipeLineContext pipeLineContext = new PipeLineContext();
            pipeLineContext.setAttr("args", Lambkit.context().getAttr("args"));
            context().getInitPipeLine().invoke(pipeLineContext);
            context().getStartPipeLine().invoke();
        }
        /////////////////////////////////////////////
        System.err.println("Lambkit Start Complete in " + timer.interval() / 1000.0 + " seconds (^_^)\n");
        return app();
    }

    public static LambkitApp start(Class<? extends LambkitApp> appClass, Class<?> target, String[] args) {
        context().createApp(LambkitContext.MAIN_APP, appClass);
        return start(target, args);
    }

    public static void stop() {
        TimeInterval timer = DateUtil.timer();
        AppRunningPipLine stopPipeLine = context().getStopPipeLine();
        if(stopPipeLine!=null) {
            stopPipeLine.invoke();
        } else {
            AppStopValve stopValve = new AppStopValve();
            stopValve.invoke(new PipeLineContext(), false);
        }
        System.err.println("Lambkit stop complete in " + timer.intervalSecond() + " seconds (^_^)\n");
    }

    public static synchronized void restart() {
        TimeInterval timer = DateUtil.timer();
        AppRunningPipLine stopPipeLine = context().getStopPipeLine();
        AppRunningPipLine startPipeLine = context().getStartPipeLine();
        if(stopPipeLine!=null && startPipeLine!=null) {
            stopPipeLine.invoke();
            startPipeLine.invoke();
        } else {
            for(LambkitApp app : context.appCollection()) {
                app.restart();
            }
        }
        System.err.println("Lambkit restart complete in " + timer.intervalSecond() + " seconds (^_^)\n");
    }

    public static void removeApp(String name) {
        context.removeApp(name);
    }

    //////////////////////////////////////////////

    /**
     * 获取配置信息
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T config(Class<T> clazz) {
        return context.getConfigCenterContainer().get(clazz);
    }

    public static <T> T config(Class<T> clazz, Class<? extends ConfigCenter> configCenterClass) {
        return context.getConfigCenterContainer().get(clazz, configCenterClass);
    }

    /**
     * 读取配置文件信息
     *
     * @param clazz
     * @param prefix
     * @param <T>
     * @return
     */
    public static <T> T config(Class<T> clazz, String prefix) {
        return context.getConfigCenterContainer().get(clazz, prefix);
    }

    /**
     * 获取配置信息
     *
     * @param key
     * @param defaultValue
     * @return
     */
    public static String config(String key, String defaultValue) {
        return configCenter().getValue(key, defaultValue);
    }

    public static ConfigCenterContainer configCenter() {
        return context.getConfigCenterContainer();
    }

    /**
     * 获取实例
     * @param <T>
     * @param clazzName
     * @return
     */
    public static <T> T get(String clazzName) {
        return context().getBeanFactory().get(ClassUtil.loadClass(clazzName));
    }

    /**
     * 获取实例
     * @param <T>
     * @param interfaceClass
     * @return
     */
    public static <T> T get(Class<T> interfaceClass) {
        return context().getBeanFactory().get(interfaceClass);
    }

    public static <T> T get(Class<T> interfaceClass, Class<? extends T> defaultClass) {
        T t = context().getBeanFactory().get(interfaceClass);
        if(t==null) {
            return context().getBeanFactory().get(defaultClass);
        }
        return t;
    }

    public static <T> void set(Class<T> clazz, T bean) {
        context().getBeanFactory().set(clazz, bean);
    }

    public static <T> void set(Class<T> interfaceClass, Class<? extends T> implementClass) {
        context().getBeanFactory().set(interfaceClass, implementClass);
    }

    public static ICache getCache() {
        return context.getCacheFactory() != null ? context.getCacheFactory().getCache() : null;
    }

    public static ICache getCache(String type) {
        return context.getCacheFactory() != null ? context.getCacheFactory().getCache(type) : null;
    }

    public static void printBanner() {
        ClassPathResource resource = new ClassPathResource("config/banner.txt");
        if(resource!=null) {
            String banner = resource.readUtf8Str();
            System.out.println(banner);
        }
    }
}
